<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:job="livcos.org/ns/process/job"
  xmlns:c="livcos.org/ns/cosmos"
  xmlns:doc="livcos.org/ns/doc"
  xmlns:u="livcos.org/ns/cosmos/update"
  xmlns:m="livcos.org/ns/map"
  xmlns:f="livcos.org/ns/xslt/function"
  exclude-result-prefixes="c job u m f">
  
  <xsl:import href="../../web/editor/Edit.xsl"/>
  
  <xsl:param name="item"/>
  <xsl:param name="label"/>
  <xsl:param name="map"/>
  <xsl:param name="rel"/>
  <xsl:param name="next"/>
  <xsl:param name="group"/>
  
	<xsl:variable name="integrityCheckObjId" select="'/livcos.org/Map/web/integrity'"/>
	
	<xsl:variable name="uri" select="f:cosmos-uri($item, /)"/>

	<xsl:template match="/">
		<xsl:apply-templates select="*/c:request/parameter/*" />
		<xsl:if test="$objId">
			<job:transform def="{$integrityCheckObjId}">
				<job:param name="obj" value="{$objId}"/>
			</job:transform>
		</xsl:if>
		<xsl:apply-templates select="*/c:request/parameter" mode="feedback"/>
	</xsl:template>
	
	<xsl:template match="parameter" mode="feedback">
		<xsl:choose>
			<xsl:when test="delete | copyInto | moveInto">
				<job:feed ref="{$view}" stream="response" base="job"/>
			</xsl:when>
			<xsl:otherwise>
				<!-- TODO: interpret simple, small JSON edit feedback to merge changes for all actions in client JavaScript -->
				<job:feed ref="{$viewObjId}_feedback.view" stream="response" base="job" content-type="text/plain"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	
	<xsl:template match="*">
  </xsl:template>
  
  <xsl:template match="update | insert | create | clone">
    <u:context ref="{$objId}" scope="content">
      <u:context>
        <xsl:attribute name="select">
          <xsl:text>.</xsl:text>
          <xsl:if test="normalize-space($id) != ''">
            <xsl:text>//*[@id = '</xsl:text>
            <xsl:value-of select="$id"/>
            <xsl:text>']</xsl:text>
          </xsl:if>
        </xsl:attribute>
        <xsl:apply-imports/>
      </u:context>
    </u:context>
  </xsl:template>
  
  <xsl:template match="entry" mode="mapTmpl">
    <xsl:param name="val"/>
    <xsl:if test="matches($val, @pattern)"><xsl:copy-of select="."/></xsl:if>
  </xsl:template>
  
  <xsl:template match="insertItem">
    <xsl:call-template name="createItem">
      <xsl:with-param name="label" select="$label"/>
      <xsl:with-param name="request" select="../.."/>
      <xsl:with-param name="uri" select="$map"/>
      <xsl:with-param name="mapId" select="$uri/obj"/>
      <xsl:with-param name="itemId" select="$uri/id"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template name="createItem">
    <xsl:param name="label"/>
    <xsl:param name="request"/>
    <xsl:param name="uri"/>
    <xsl:param name="mapId"/>
    <xsl:param name="itemId"/>
    <xsl:param name="note"/>
    <xsl:variable name="cntxt" select="substring-before($request/url, $request/access/@uri)"/>
    <xsl:variable name="toUri"
      select="if (starts-with($uri, $cntxt)) then substring-after($uri, $cntxt) else resolve-uri($uri, $mapId)"/>
    <xsl:variable name="mapObj" select="document($mapId)/*"/>
    <xsl:variable name="_item" select="$mapObj/m:body/m:item[@id = $itemId]"/>
    <xsl:variable name="item" select="if ($_item) then $_item else $mapObj/m:body/m:item[1]"/>
    <xsl:if test="($label != $item/m:label) or ($toUri != $mapId)">
      <!-- test for template -->
      <xsl:variable name="mappedTmplIds">
        <xsl:apply-templates select="document('../data/templates/mappings')/*/*" mode="mapTmpl">
          <xsl:with-param name="val" select="$toUri"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:variable name="params"
        select="concat('?label=', encode-for-uri($label), '&amp;uri=', encode-for-uri($toUri), '&amp;item=', $item/@id)"/>
      <xsl:variable name="tmplUri" select="concat($mappedTmplIds/*[1]/@ref, $params)"/>
      <xsl:choose>
        <xsl:when test="contains($toUri, '://')">
          <!-- URL link -->
          <xsl:variable name="toItem" select="$mapObj/m:body/*[m:label = $label]"/>
          <xsl:variable name="toLinkItem" select="$mapObj/m:body/*[doc:link/@ref = $toUri]"/>
          <xsl:choose>
            <xsl:when test="$toItem">
              <!-- existing item -->
              <xsl:call-template name="insertLink">
                <xsl:with-param name="item" select="$toItem"/>
                <xsl:with-param name="link" select="$toUri"/>
                <xsl:with-param name="note" select="$note"/>
              </xsl:call-template>
              <xsl:call-template name="addRel">
                <xsl:with-param name="item" select="$item"/>
                <xsl:with-param name="ref" select="concat('#', $toItem/@id)"/>
              </xsl:call-template>
            </xsl:when>
            <xsl:when test="$toLinkItem">
              <!-- existing item -->
              <xsl:call-template name="insertLink">
                <xsl:with-param name="item" select="$toLinkItem"/>
                <xsl:with-param name="link" select="$toUri"/>
                <xsl:with-param name="note" select="$note"/>
              </xsl:call-template>
              <xsl:call-template name="addRel">
                <xsl:with-param name="item" select="$item"/>
                <xsl:with-param name="ref" select="concat('#', $toLinkItem/@id)"/>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <!-- new item -->
              <u:context ref="{$mapId}" scope="content">
                <u:insert select="m:body">
                  <u:namespace prefix="m" uri="livcos.org/ns/map"/>
                  <xsl:call-template name="defItem">
                    <xsl:with-param name="label" select="$label"/>
                    <xsl:with-param name="link" select="$toUri"/>
                    <xsl:with-param name="note" select="$note"/>
                    <xsl:with-param name="ref" select="concat('#', $item/@id)"/>
                  </xsl:call-template>
                </u:insert>
              </u:context>
              <xsl:call-template name="addRel">
                <xsl:with-param name="item" select="$item"/>
                <xsl:with-param name="ref" select="'#'"/>
              </xsl:call-template>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="$mappedTmplIds/*[1] and doc-available($tmplUri)">
          <!-- mapped template -->
          <!-- if a valid template has been found, we expect it to be an item, linking to the toURI
               and it should be inserted into the referencing map (thus it is not possible to specify
               a template for a new item in a foreign map) -->
          <xsl:call-template name="insertLinkByTemplate">
            <xsl:with-param name="tmpl" select="doc($tmplUri)/*"/>
            <xsl:with-param name="mapObj" select="$mapObj"/>
            <xsl:with-param name="item" select="$item"/>
            <xsl:with-param name="toUri" select="$toUri"/>
            <xsl:with-param name="note" select="$note"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:when test="doc-available($toUri)">
          <!-- existing object -->
          <xsl:variable name="toObj" select="document($toUri)/*"/>
          <xsl:choose>
            <xsl:when test="namespace-uri($toObj) = 'livcos.org/ns/map'">
              <!-- existing map -->
              <xsl:variable name="toItem" select="$toObj/m:body/m:item[m:label = $label][1]"/>
              <xsl:if test="not($toItem)">
                <!-- new item -->
                <u:context ref="{$toUri}" scope="content">
                  <u:insert select="m:body">
                    <u:namespace prefix="m" uri="livcos.org/ns/map"/>
                    <xsl:variable name="mapRef" select="if ($toUri != $mapId) then $mapId else ''"/>
                    <xsl:call-template name="defItem">
                      <xsl:with-param name="label" select="$label"/>
                      <xsl:with-param name="note" select="$note"/>
                      <xsl:with-param name="ref" select="concat($mapRef, '#', $item/@id)"/>
                    </xsl:call-template>
                  </u:insert>
                </u:context>
              </xsl:if>
              <xsl:variable name="toMapRef" select="if ($toUri != $mapId) then $toUri else ''"/>
              <xsl:call-template name="addRel">
                <xsl:with-param name="item" select="$item"/>
                <xsl:with-param name="ref" select="concat($toMapRef, '#', $toItem/@id)"/>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <!-- link to object -->
              <xsl:variable name="tmpl">
                <!-- build a potential template -->
                <xsl:variable name="typeUri" select="concat(namespace-uri($toObj), '/', local-name($toObj))"/>
                <xsl:variable name="tmplUri" select="concat('/livcos.org/Map/data/templates/', $typeUri, $params)"/>
                <xsl:choose>
                  <xsl:when test="doc-available($tmplUri)">
                    <xsl:copy-of select="doc($tmplUri)/*"/>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:call-template name="defItem">
                      <xsl:with-param name="label" select="$label"/>
                      <xsl:with-param name="link" select="$toUri"/>
                      <xsl:with-param name="note" select="$note"/>
                      <xsl:with-param name="ref" select="concat('#', $item/@id)"/>
                    </xsl:call-template>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:variable>
              <xsl:call-template name="insertLinkByTemplate">
                <xsl:with-param name="tmpl" select="$tmpl/*"/>
                <xsl:with-param name="mapObj" select="$mapObj"/>
                <xsl:with-param name="item" select="$item"/>
                <xsl:with-param name="toUri" select="$toUri"/>
                <xsl:with-param name="note" select="$note"/>
              </xsl:call-template>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <!-- new item in new map -->
          <u:insert ref="{$toUri}" create-path="true">
            <map xmlns="livcos.org/ns/map">
              <body>
                <xsl:call-template name="defItem">
                  <xsl:with-param name="label" select="$label"/>
                  <xsl:with-param name="note" select="$note"/>
                  <xsl:with-param name="ref" select="concat($mapId, '#', $item/@id)"/>
                </xsl:call-template>
              </body>
            </map>
          </u:insert>
          <xsl:call-template name="addRel">
            <xsl:with-param name="item" select="$item"/>
            <xsl:with-param name="ref" select="concat($toUri, '#item_1')"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>
  
  <xsl:template name="insertLinkByTemplate">
    <xsl:param name="tmpl"/>
    <xsl:param name="mapObj"/>
    <xsl:param name="item"/>
    <xsl:param name="toUri"/>
    <xsl:param name="note"/>
    <xsl:variable name="toItem" select="$mapObj/m:body/*[m:label = $tmpl/m:label][1]"/>
    <xsl:choose>
      <xsl:when test="$toItem">
        <!-- existing item -->
        <xsl:call-template name="insertLink">
          <xsl:with-param name="item" select="$toItem"/>
          <xsl:with-param name="link" select="$toUri"/>
          <xsl:with-param name="note" select="$note"/>
        </xsl:call-template>
        <xsl:variable name="itemRef" select="concat('#', $item/@id)"/>
        <xsl:if test="($toItem/@id != $item/@id) and not($toItem/m:rels/m:rel[@ref = $itemRef])">
          <u:context ref="{f:object-id($toItem)}" scope="content">
            <u:context select="m:body/*[@id = '{$toItem/@id}']">
              <u:namespace prefix="m" uri="livcos.org/ns/map"/>
              <xsl:if test="not($toItem/m:rels[@group = 'main'])">
                <u:insert select=".">
                  <rels id="{{new-id()}}" group="main" xmlns="livcos.org/ns/map"/>
                </u:insert>
              </xsl:if>
              <u:insert select="m:rels[@group = 'main']">
                <u:namespace prefix="m" uri="livcos.org/ns/map"/>
                <rel id="{{new-id()}}" ref="{$itemRef}" xmlns="livcos.org/ns/map"/>
              </u:insert>
            </u:context>
          </u:context>
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <!-- new item -->
        <u:context ref="{f:object-id($mapObj)}" scope="content">
          <u:insert select="m:body">
            <u:namespace prefix="m" uri="livcos.org/ns/map"/>
            <xsl:copy-of select="$tmpl"/>
          </u:insert>
          <xsl:if test="normalize-space($note) != ''">
            <u:insert select="m:body/m:item[last()]">
              <u:namespace prefix="m" uri="livcos.org/ns/map"/>
              <doc:paragraph id="{{new-id()}}">
                <xsl:value-of select="$note"/>
              </doc:paragraph>
            </u:insert>
          </xsl:if>
        </u:context>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:call-template name="addRel">
      <xsl:with-param name="item" select="$item"/>
      <xsl:with-param name="ref" select="concat('#', $toItem/@id)"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template name="insertLink">
    <xsl:param name="item"/>
    <xsl:param name="link"/>
    <xsl:param name="note"/>
    <xsl:if test="not($item/doc:link[@ref = $link])">
      <u:context ref="{f:object-id($item)}" scope="content">
        <u:insert select="m:body/*[@id = '{$item/@id}']">
          <u:namespace prefix="m" uri="livcos.org/ns/map"/>
          <doc:link id="{{new-id()}}" ref="{$link}"/>
        </u:insert>
      </u:context>
    </xsl:if>
    <xsl:if test="(normalize-space($note) != '') and not($item/doc:paragraph[. = $note])">
      <u:context ref="{f:object-id($item)}" scope="content">
        <u:insert select="m:body/*[@id = '{$item/@id}']">
          <u:namespace prefix="m" uri="livcos.org/ns/map"/>
          <doc:paragraph id="{{new-id()}}"><xsl:value-of select="$note"/></doc:paragraph>
        </u:insert>
      </u:context>
    </xsl:if>
  </xsl:template>
  
  <xsl:template name="defItem">
    <xsl:param name="label"/>
    <xsl:param name="link"/>
    <xsl:param name="note"/>
    <xsl:param name="ref"/>
    <item id="{{new-id()}}" xmlns="livcos.org/ns/map">
      <label>
        <xsl:value-of select="$label"/>
        <xsl:if test="normalize-space($label) = ''">
          <xsl:value-of select="$link"/>
        </xsl:if>
      </label>
      <xsl:if test="normalize-space($link) != ''">
        <doc:link id="{{new-id()}}" ref="{$link}"/>
      </xsl:if>
      <xsl:if test="normalize-space($note) != ''">
        <doc:paragraph id="{{new-id()}}">
          <xsl:value-of select="$note"/>
        </doc:paragraph>
      </xsl:if>
      <rels id="{{new-id()}}" group="sys">
        <rel id="{{new-id()}}" ref="{$ref}"/>
      </rels>
    </item>
  </xsl:template>
  
  <xsl:template name="addRel">
    <xsl:param name="item"/>
    <xsl:param name="ref"/>
    <xsl:if test="($ref != concat('#', $item/@id)) and not($item/m:rels/m:rel[@ref = $ref])">
      <u:context ref="{f:object-id($item)}" scope="content">
        <u:context select="m:body/*[@id = '{$item/@id}']">
          <u:namespace prefix="m" uri="livcos.org/ns/map"/>
          <xsl:if test="not($item/m:rels[@group = 'main'])">
            <u:insert select=".">
              <rels id="{{new-id()}}" group="main" xmlns="livcos.org/ns/map"/>
            </u:insert>
          </xsl:if>
          <u:insert select="m:rels[@group = 'main']">
            <u:namespace prefix="m" uri="livcos.org/ns/map"/>
            <xsl:call-template name="defRel">
              <xsl:with-param name="ref" select="$ref"/>
            </xsl:call-template>
          </u:insert>
        </u:context>
      </u:context>
    </xsl:if>
  </xsl:template>
  
  <xsl:template name="defRel">
    <xsl:param name="ref"/>
    <rel id="{{new-id()}}" xmlns="livcos.org/ns/map">
      <u:attribute name="ref">
        <xsl:value-of select="$ref"/>
        <xsl:if test="ends-with($ref, '#')">
          <xsl:choose>
            <xsl:when test="$ref = '#'">
              <u:copy-of select="../../*[last()]/@id"/>
            </xsl:when>
            <xsl:otherwise>
              <u:copy-of ref="{substring-before($ref, '#')}" scope="content" select="m:body/*[last()]/@id">
                <u:namespace prefix="m" uri="livcos.org/ns/map"/>
              </u:copy-of>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:if>
      </u:attribute>
    </rel>
  </xsl:template>
  
  <xsl:template match="reorder">
    <xsl:variable name="_map" select="document($map)/*"/>
    <xsl:variable name="_rel" select="$_map/m:body/*/*/*[@id = $rel]"/>
    <xsl:variable name="_item" select="$_rel/../.."/>
    <u:context ref="{$map}" scope="content">
      <u:context select="m:body/*[@id = '{$_item/@id}']">
        <u:namespace prefix="m" uri="livcos.org/ns/map"/>
        <xsl:variable name="grp" select="$_item/m:rels[@group = $group]"/>
        <xsl:variable name="_next" select="$_rel/following-sibling::*[1]"/>
        <xsl:choose>
          <xsl:when test="$grp and $next">
            <xsl:if test="not($_next) or ($_next/@id != $next)">
              <u:context select="m:rels[@group = '{$group}']">
                <u:namespace prefix="m" uri="livcos.org/ns/map"/>
                <u:info><move/></u:info>
                <u:insert select="*[@id = '{$next}']" position="before">
                  <u:move-from select="../../*/*[@id = '{$rel}']"/>
                </u:insert>
              </u:context>
            </xsl:if>
          </xsl:when>
          <xsl:when test="$grp">
            <xsl:if test="$_next or ($_rel/../@group != $group)">
              <u:context select="m:rels[@group = '{$group}']">
                <u:namespace prefix="m" uri="livcos.org/ns/map"/>
                <u:info><move/></u:info>
                <u:insert select=".">
                  <u:move-from select="../*/*[@id = '{$rel}']"/>
                </u:insert>
              </u:context>
            </xsl:if>
          </xsl:when>
          <xsl:otherwise>
            <u:info><move/></u:info>
            <u:insert select=".">
              <rels id="{{new-id()}}" group="{$group}" xmlns="livcos.org/ns/map">
                <u:move-from select="*/*[@id = '{$rel}']"/>
              </rels>
            </u:insert>
          </xsl:otherwise>
        </xsl:choose>
      </u:context>
    </u:context>
  </xsl:template>
  
	<xsl:template match="delete">
		<xsl:variable name="_objUri" select="concat($objId, '.content')"/>
		<xsl:if test="doc-available($_objUri)">
			<xsl:apply-templates select="doc($_objUri)/*//*[@id = $id]" mode="delete"/>
		</xsl:if>
		<u:context ref="{$objId}" scope="content">
			<u:context>
				<xsl:attribute name="select">
					<xsl:text>.</xsl:text>
					<xsl:if test="normalize-space($id) != ''">
						<xsl:text>//*[@id = '</xsl:text>
						<xsl:value-of select="$id"/>
						<xsl:text>']</xsl:text>
					</xsl:if>
				</xsl:attribute>
				<xsl:apply-imports/>
			</u:context>
		</u:context>
	</xsl:template>
	
	<xsl:template match="*" mode="delete">
	</xsl:template>
	
	<xsl:template match="m:item" mode="delete">
		<xsl:apply-templates select="m:rels/m:rel" mode="deleteItem"/>
	</xsl:template>
	
	<xsl:template match="m:rel" mode="deleteItem">
		<xsl:variable name="_objId">
			<xsl:choose>
				<xsl:when test="starts-with(@ref, '#')">
					<xsl:value-of select="$objId"/>
				</xsl:when>
				<xsl:otherwise>
					<xsl:value-of select="substring-before(@ref, '#')"/>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
		<xsl:variable name="_objUri" select="concat($_objId, '.content')"/>
		<xsl:if test="doc-available($_objUri)">
			<xsl:variable name="_backRef">
				<xsl:if test="$_objId != $objId">
					<xsl:value-of select="$objId"/>
				</xsl:if>
				<xsl:text>#</xsl:text>
				<xsl:value-of select="../../@id"/>
			</xsl:variable>
			<xsl:variable name="_item" select="doc($_objUri)/*/m:body/m:item[@id = substring-after(current()/@ref, '#')]"/>
			<xsl:variable name="_rel" select="$_item/m:rels/m:rel[@ref = $_backRef]"/>
			<xsl:if test="$_rel">
				<u:delete ref="{$_objId}" scope="content" select="*//*[@id = '{$_rel/@id}']"/>
			</xsl:if>
		</xsl:if>
	</xsl:template>
	
	<xsl:template match="copyInto | moveInto">
    <xsl:variable name="_map" select="doc($objId)/*"/>
    <xsl:variable name="_rel" select="$_map/m:body/*/*/*[@id = $rel]"/>
    <xsl:variable name="_targetRel" select="$_map/m:body/*/*/*[@id = $id]"/>
    <xsl:variable name="_targetUri" select="f:cosmos-uri($_targetRel/@ref, $_targetRel)"/>
    <xsl:variable name="_target" select="doc($_targetUri/obj)/*/m:body/*[@id = $_targetUri/id]"/>
    <xsl:variable name="_absRef">
      <xsl:if test="starts-with($_rel/@ref, '#')"><xsl:value-of select="$objId"/></xsl:if>
      <xsl:value-of select="$_rel/@ref"/>
    </xsl:variable>
    <xsl:variable name="_ref">
      <xsl:choose>
        <xsl:when test="starts-with($_absRef, $_targetUri/obj)">
          <xsl:value-of select="substring($_absRef, string-length($_targetUri/obj) + 1)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$_absRef"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:if test="not($_target/m:rels/m:rel[@ref = $_ref])">
      <u:context ref="{$_targetUri/obj}" scope="content">
        <u:context select="m:body//*[@id = '{$_targetUri/id}']">
          <u:namespace prefix="m" uri="livcos.org/ns/map"/>
          <xsl:if test="not($_target/m:rels[@group = 'main'])">
            <u:insert select=".">
              <rels id="{{new-id()}}" group="main" xmlns="livcos.org/ns/map"/>
            </u:insert>
          </xsl:if>
          <u:insert select="m:rels[@group = 'main']">
            <u:namespace prefix="m" uri="livcos.org/ns/map"/>
            <rel id="{$_rel/@id}" ref="{$_ref}" xmlns="livcos.org/ns/map"/>
         </u:insert>
        </u:context>
      </u:context>
    </xsl:if>
    <xsl:if test="local-name(.) = 'moveInto'">
      <u:context ref="{$objId}" scope="content">
        <u:delete select="m:body//*[@id = '{$rel}']">
          <u:namespace prefix="m" uri="livcos.org/ns/map"/>
        </u:delete>
      </u:context>
    </xsl:if>
  </xsl:template>
  
</xsl:stylesheet>
